﻿using System;
using System.Collections.Generic;
using ZyGame.Execute;
using System.Collections;
using UnityEngine;
using ZyGame.Transport;
using System.IO;

namespace ZyGame.Resource
{
    public sealed class LoadingBundleRunnable : Runnable
    {
        private string mainName;
        private Action<float> progressCallback;
        private List<AssetBundle> bundles;
        private int count;
        private int loadingCount = 0;
        private bool success = true;


        public static LoadingBundleRunnable Create(string mainName, params PackageInfo[] list)
        {
            string runnableName = $"[MODULE LOADING]{mainName}";
            if (Client.Running.TryGetOrCreate(runnableName, out LoadingBundleRunnable loadingBundleRunnable))
            {
                return loadingBundleRunnable;
            }

            loadingBundleRunnable.success = true;
            loadingBundleRunnable.count = list.Length;
            loadingBundleRunnable.mainName = mainName;
            loadingBundleRunnable.bundles = new List<AssetBundle>();
            loadingBundleRunnable.Execute(loadingBundleRunnable.CheckLoadBundleCompletion);
            loadingBundleRunnable.Execute(loadingBundleRunnable.Completion);
            for (int i = 0; i < list.Length; i++)
            {
                Client.Tools.StartCoroutine(loadingBundleRunnable.OnStartLoadPackage(list[i]));
            }

            return loadingBundleRunnable;
        }

        private IEnumerator CheckLoadBundleCompletion()
        {
            yield return new WaitUntil(CheckState);
        }

        private bool CheckState()
        {
            progressCallback?.Invoke(loadingCount / (float)count);
            return loadingCount == count;
        }

        public LoadingBundleRunnable SetProgressCallback(Action<float> action)
        {
            progressCallback = action;
            return this;
        }

        private void Completion()
        {
            if (success is false)
            {
                for (int i = 0; i < bundles.Count; i++)
                {
                    bundles[i].Unload(true);
                }

                return;
            }

            for (int i = 0; i < bundles.Count; i++)
            {
                //这里判断一下，因为有可能同步加载完了，导致异步加载出错
                if (RefrenceManager.instance.EnsureAssetBundleLoaded(bundles[i].name))
                {
                    continue;
                }

                BundleRefrence refrence = new BundleRefrence(bundles[i]);
                if (refrence.name == mainName)
                {
                    SetResult(refrence);
                }

                RefrenceManager.instance.Enqueue(refrence);
            }

            LoadingManager.instance.Remove(mainName);
        }

        private void SetFail()
        {
            success = false;
            loadingCount++;
        }

        private IEnumerator OnStartLoadPackage(PackageInfo package)
        {
            string fileName = Path.GetFileName(package.name);
            byte[] bytes = Array.Empty<byte>();
            if (!Client.File.Exists(fileName, package.version, package.crc))
            {
                DownloadContext download = default;
                if (package.name.StartsWith("http"))
                    download = new DownloadContext(package.name, package.version, package.crc);
                else
                    download = new DownloadContext(package);
                yield return download.Start();
                if (download.state is not DownloadState.Completed)
                {
                    SetFail();
                    SetResult(new Exception("加载资源失败"));
                    yield break;
                }

                Client.File.WriterData(fileName, download.bytes, package.version, package.crc);
                bytes = download.bytes;
            }
            else
            {
                Runnable<byte[]> runnable = Client.File.ReadDataAsync(fileName, package.version, package.crc);
                yield return runnable.Waitting();
                if (runnable.EnsureExecutedSuccessful() is false)
                {
                    SetFail();
                    SetResult(new Exception("加载资源失败"));
                    yield break;
                }

                bytes = runnable.result;
            }

            if (bytes is null || bytes.Length is 0)
            {
                SetFail();
                SetResult(new Exception("加载资源失败"));
                yield break;
            }

            AssetBundleCreateRequest createRequest = AssetBundle.LoadFromMemoryAsync(bytes, package.crc);
            yield return createRequest;
            if (createRequest.isDone is false)
            {
                SetFail();
                SetResult(new Exception("加载资源失败"));
                yield break;
            }

            loadingCount++;
            if (RefrenceManager.instance.EnsureAssetBundleLoaded(fileName))
            {
                yield break;
            }

            bundles.Add(createRequest.assetBundle);
        }
    }
}